package com.i72.freeway;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author jiangj
 * @version 1.0.0
 * @ClassName Proxy.java
 * @Description TODO
 * @createTime 2021年12月28日 14:54:00
 */
@Slf4j
public class Proxy {


    /**
     * 获取代理类列表
     * @param clazz api接口类
     * @param freewayVersionProperties 配置
     * @return key=beanName, value=proxyClass
     */
    public static List<ProxyModel> getProxy(Class clazz) {
        List<ProxyModel> proxyClasses = new ArrayList<>();
        /*
        if (freewayVersionProperties != null) {
            Map<String, List<ApiVersion>> apiVersionMap = freewayVersionProperties.getApiVersionMap();
            if (apiVersionMap.get(clazz.getName()) != null) {
                for (ApiVersion apiVersion : apiVersionMap.get(clazz.getName())) {
                    proxyClasses.add(new ProxyModel(apiVersion.getBeanName(), clazz, getProxy(clazz, apiVersion)));
                }
            }
            if (proxyClasses.size() > 0) return proxyClasses;
        }*/
        proxyClasses.add(new ProxyModel(ClassUtils.getAppNode(clazz) + clazz.getSimpleName(), clazz, getProxy(clazz, null)));
        return proxyClasses;
    }

    public static Class getProxy(Class clazz,Object apiVersion) {
        try {
            ClassGenerator classGenerator = new ClassGenerator(clazz.getClassLoader());

            String proxyClassName;
            /*
            if (apiVersion == null) {
                proxyClassName = clazz.getName() + "$proxy";
            } else {
                proxyClassName = clazz.getName() + apiVersion.getVersion() + "$proxy";
            }*/
            proxyClassName = clazz.getName() + "$proxy";
            classGenerator.setClassName(proxyClassName);
            classGenerator.addField("public static " + HandleParam.class.getName() + "[] handleParams;");
            classGenerator.addField("public static Class[] returnTypes;");
            classGenerator.addField("private " + HttpHandler.class.getName() + " handler = new " + HttpHandler.class.getName() + "();");
            classGenerator.addInterface(clazz);

            Method[] methods = clazz.getMethods();
            HandleParam[] handleParams = new HandleParam[methods.length];
            Class[] returnTypes = new Class[methods.length];
            for (int i = 0; i < methods.length; i++) {
                handleParams[i] = HandleParam.getHandleParam(clazz, methods[i]);
                returnTypes[i] = methods[i].getReturnType();

                StringBuilder code = new StringBuilder();
                code.append("Object[] args = $args;");
                code.append("Object result = handler.handle(handleParams[").append(i).append("], args, returnTypes[").append(i).append("]);");
                if (!Void.TYPE.equals(returnTypes[i])) code.append("return ").append(getReturnCode(returnTypes[i], "result")).append(";");

                classGenerator.addMethod(methods[i], code.toString());
            }

            Class proxyClass = classGenerator.toClass();

            proxyClass.getField("returnTypes").set(null, returnTypes);
            proxyClass.getField("handleParams").set(null, handleParams);
            return proxyClass;
        } catch (Exception e) {
            log.error("generate proxyClass {} failure", clazz.getName(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 转换方法返回参数类型
     */
    private static String getReturnCode(Class clazz, String name) {
        if (clazz.isPrimitive()) {
            if (Boolean.TYPE == clazz) return name + "==null?false:((Boolean)" + name + ").booleanValue()";
            if (Byte.TYPE == clazz) return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
            if (Character.TYPE == clazz) return name + "==null?(char)0:((Character)" + name + ").charValue()";
            if (Double.TYPE == clazz) return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
            if (Float.TYPE == clazz) return name + "==null?(float)0:((Float)" + name + ").floatValue()";
            if (Integer.TYPE == clazz) return name + "==null?(int)0:((Integer)" + name + ").intValue()";
            if (Long.TYPE == clazz) return name + "==null?(long)0:((Long)" + name + ").longValue()";
            if (Short.TYPE == clazz) return name + "==null?(short)0:((Short)" + name + ").shortValue()";
            throw new RuntimeException(name + " is unknown primitive type.");
        }
        return "(" + ReflectUtils.getName(clazz) + ")" + name;
    }

}
